/*
 * Copyright (C) 2007-2008  Hylke van der Schaaf
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, in version 3 of the License.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package hextd.UI;

import hextd.Constants;
import hextd.HexTD.GameState;
import hextd.core.ContextNormal;
import hextd.core.ContextNormal.AuthStatus;
import hextd.core.ContextPlayer;
import hextd.server.UserInfo;
import hextd.util.HTMLEncode;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Timer;
import java.util.TimerTask;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.DefaultListModel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.border.TitledBorder;
import javax.swing.text.html.HTMLEditorKit;
import javax.swing.text.html.StyleSheet;
import com.usoog.tdcore.UserInfo;
import com.usoog.tdcore.gamemanager.GameManager;
import com.usoog.tdcore.message.FactoryMessage;
import com.usoog.tdcore.message.Message;
import com.usoog.tdcore.message.MessageError;
import com.usoog.tdcore.message.MessageFetch;
import com.usoog.tdcore.message.MessageGameCreate;
import com.usoog.tdcore.message.MessageGameJoin;
import com.usoog.tdcore.message.MessageGameList;
import com.usoog.tdcore.message.MessageGameList.GameInfo;
import com.usoog.tdcore.message.MessageKickUser;
import com.usoog.tdcore.message.MessagePing;
import com.usoog.tdcore.message.MessagePlayerForfeit;
import com.usoog.tdcore.message.MessagePong;
import com.usoog.tdcore.message.MessageReplay;
import com.usoog.tdcore.message.MessageSay;
import com.usoog.tdcore.message.MessageSettings;
import com.usoog.tdcore.message.MessageUnknown;
import com.usoog.tdcore.message.MessageUserAuth;
import com.usoog.tdcore.message.MessageUserInGame;
import com.usoog.tdcore.message.MessageUserInfo;
import com.usoog.tdcore.message.MessageUserList;
import com.usoog.tdcore.network.LineListener;
import com.usoog.tdcore.network.NetworkClient;
import com.usoog.tdcore.network.NetworkConnection;
import com.usoog.tdcore.network.NetworkConnector;
import com.usoog.tdcore.network.ConnectionListener;

/**
 *
 * @author  hylke
 */
public class PanelNetConnection extends javax.swing.JPanel implements ConnectionListener, NetworkConnector {

	/**
	 * Commands are used to encapsulate code into something that can be put in a
	 * Map
	 */
	private interface MessageCommand {

		public void execute(Message message);
	}
	private Map<String, MessageCommand> messageCommands = new HashMap<String, MessageCommand>();
	//
	private ContextNormal context;
	private GameManager gameManager;
	private StringBuilder htmlContent;
	private NetworkClient client;
	private String serverAddress;
	private int serverPort;
	private FactoryMessage messageFactory;
	//
	private List<GameInfo> games = new ArrayList<GameInfo>();
	private Map<Integer, GameInfo> gamesById = new HashMap<Integer, GameInfo>();
	private List<UserInfo> users = new ArrayList<UserInfo>();
	private Map<Integer, UserInfo> usersByUserId = new HashMap<Integer, UserInfo>();
	private Map<Integer, UserInfo> usersByPlayerId = new HashMap<Integer, UserInfo>();
	private Integer channelId;
	private String channelName;
	private final UserInfo localUserInfo = new UserInfo();
	private int playerGameId;
	private String status = "Not connected to server.";
	//
	private Timer pingTimer;
	private TimerTask pingTask;
	//
	private HTMLEditorKit editorkit;
	private PlayerListRenderer plr;
	//
	private String sendLater;
	private boolean keepTextBar = true;
	//
	private boolean wasConnected = false;
	private boolean keepFullLog = false;
	private StringBuilder fullLog = new StringBuilder();

	/** Creates new form PanelNetConnection */
	public PanelNetConnection() {
		gamesById = new HashMap<Integer, GameInfo>();
		editorkit = new HTMLEditorKit();
	}

	public void setContext(GameManager gm) {
		gameManager = gm;
		messageFactory = gm.getMessageFactory();
		doInit();
		gm.setNetworkConnector(this);

		StyleSheet s = context.loadStyleSheet("styles.css");
		editorkit.setStyleSheet(s);
		jTextPane_output.setEditorKit(editorkit);
	}

	private void doInit() {
		initComponents();
		fillMessageMap();
		plr = new PlayerListRenderer(context.colors);
		jListPlayerList.setCellRenderer(plr);
		jPanelInLobby.setVisible(false);
		jButtonExit.setVisible(false);
		setNoConnection();
		htmlContent = new StringBuilder();
	}

	public JPanel getChannelList() {
		return jPanelChannels;
	}

	/**
	 * Checks if the game has progressed enough that leaving means forfeiting.
	 * Presents the user with a question if this is the game.
	 * @return true if user really wants to leave
	 */
	public boolean optionalForfeitGame() {
		switch (context.getGameState()) {
			case mpPlaying:
			case mpCatchup:
				if (context.getGameTime().getTime() > 6000) {
					Object[] options = {"Yes, forfeit",
						"No, keep playing"};
					int n = JOptionPane.showOptionDialog(this,
							"Leaving now will cause you to lose this game?",
							"Really leave?",
							JOptionPane.YES_NO_OPTION,
							JOptionPane.QUESTION_MESSAGE,
							null, //do not use a custom Icon
							options, //the titles of buttons
							options[0]);
					if (n == JOptionPane.YES_OPTION) {
						sendLine(new MessagePlayerForfeit(playerGameId, context.getGameTime().getTime()).getMessage());
						return true;
					}
					return false;
				}
		}
		return true;
	}

	public void exitGame() {
		if (optionalForfeitGame()) {
			context.cleanContext();
			sendLine(MessageGameJoin.formatMessage(0, 0));
		}
	}

	public void setServerAddress(String address, int port) {
		serverAddress = address;
		serverPort = port;
		startClient();
	}

	public void startClient() {
		if (client == null && context != null) {
			status = "Connecting to server...";
			client = new NetworkClient(this);
			client.setPort(serverPort);
			client.setServer(serverAddress);
			client.connect();
			context.clientStarted();
		}
	}

	private void doJoin() {
		Object value = jListChannelList.getSelectedValue();
		if (value != null && value instanceof GameInfo) {
			GameInfo info = (GameInfo) value;
			sendLine(new MessageGameJoin(localUserInfo.userId, info.id).getMessage());
		}
	}

	private void setNoConnection() {
		jTextField_send.setEnabled(false);
		jButton_send.setEnabled(false);
	}

	public void stop() {
		if (client != null) {
			client.stopClient();
		}
	}

	public void updatePlayerNames() {
		DefaultListModel m = (DefaultListModel) jListPlayerList.getModel();
		m.clear();
		for (int i = 0; i < users.size(); i++) {
			UserInfo info = users.get(i);
			m.addElement(info);
			ContextPlayer p = context.getPlayer(info.playerId);
			if (p != null) {
				p.setPlayerName(info.name);
				p.setUserId(info.userId);
			}
		}
	}

	public void updateChannelList() {
		if (!SwingUtilities.isEventDispatchThread()) {
			SwingUtilities.invokeLater(new Runnable() {

				@Override
				public void run() {
					updateChannelList();
				}
			});
		} else {
			DefaultListModel m = (DefaultListModel) jListChannelList.getModel();
			m.clear();
			gamesById.clear();
			for (GameInfo game : games) {
				gamesById.put(game.id, game);
				if (game.id != 0) {
					m.addElement(game);
				}
			}
			setChannelTitle();
		}
	}

	public int getConnectedPlayerCount() {
		return users.size();
	}

	private void setChannelTitle() {
		GameInfo info = gamesById.get(channelId);
		if (info != null) {
			channelName = info.name;
		}
		TitledBorder b = (TitledBorder) jPanelChat.getBorder();
		b.setTitle("Chat: " + channelName);
		jPanelChat.repaint();
	}

	private void fillMessageMap() {
		messageCommands.put(MessageUserList.key, new MessageCommand() {

			public void execute(Message message) {
				MessageUserList mul = (MessageUserList) message;
				users = mul.getUsers();
				usersByUserId.clear();
				usersByPlayerId.clear();
				for (UserInfo info : users) {
					usersByUserId.put(info.userId, info);
					usersByPlayerId.put(info.playerId, info);
				}
				updatePlayerNames();
			}
		});
		messageCommands.put(MessageGameList.key, new MessageCommand() {

			public void execute(Message message) {
				MessageGameList mgl = (MessageGameList) message;
				games = mgl.getGames();
				updateChannelList();
			}
		});
		messageCommands.put(MessageUserInGame.key, new MessageCommand() {

			public void execute(Message message) {
				context.getBlackBox().clear();
				context.getGameLoop().setRunning(false);
				MessageUserInGame muig = (MessageUserInGame) message;
				channelId = muig.getChannelId();

				playerGameId = muig.getPlayerId();
				context.setLocalPlayerId(playerGameId);
				System.out.println("PanelNetConnection::textReceived: Channel: " + channelId + " Player: " + playerGameId);
				setChannelTitle();
				jTextField_send.setEnabled(true);
				jButton_send.setEnabled(true);

				if (channelId == 0) {
					jPanelChannels.setVisible(true);
					jPanelInLobby.setVisible(true);
					jButtonExit.setVisible(false);
					context.setGameState(GameState.mpConnect);
					jButtonKick.setVisible(false);
					plr.setLongFormat(true);
				} else {
					GameInfo info = gamesById.get(channelId);
					channelName = info.name;
					jPanelInLobby.setVisible(false);
					jButtonExit.setVisible(true);
					plr.setLongFormat(true);
					switch (context.getGameState()) {
						case mpConnect:
						case mpSelecting:
						case mpClientSelecting:
							if (muig.isRunning()) {
								context.setGameState(GameState.mpJoinRunning);
								//TODO: Send target tick to catchupLoop
								//GameLoopImplementation.GliMpCatchup catchUpLoop = (GliMpCatchup) GameState.mpCatchup.gameLoop;
								//catchUpLoop.setTargetTick(muig.getLastTick());
							} else {
								if (muig.getPlayerId() == 0) {
									context.setGameState(GameState.mpSelecting);
								} else {
									context.setGameState(GameState.mpClientSelecting);
								}
							}
							break;
					}
					if (muig.getPlayerId() == 0) {
						jButtonKick.setVisible(true);
					} else {
						jButtonKick.setVisible(false);
					}
				}
			}
		});
		messageCommands.put(MessageSay.key, new MessageCommand() {

			public void execute(Message message) {
				MessageSay ms = (MessageSay) message;
				int speakerId = ms.getSenderId();
				UserInfo speaker = usersByUserId.get(speakerId);
				if (speaker != null) {
					int gameId = speaker.playerId;
					int relId = gameId % 6;
					String cls = "p" + relId;
					addOutputText("<span class='" + cls + "'>" + speaker.name + "</span>: " + HTMLEncode.encode(ms.getText()) + "<br>");
				} else {
					addOutputText(ms.getText());
				}
			}
		});
		messageCommands.put(MessageUserInfo.key, new MessageCommand() {

			public void execute(Message message) {
				MessageUserInfo mui = (MessageUserInfo) message;
				localUserInfo.updateFrom(mui.getUserInfo(), true);
				status = "Connected to server and authenticated. Multiplayer available.";
				context.setAuthenticated(AuthStatus.success);
				doSendLater();
			}
		});
		messageCommands.put(MessageError.key, new MessageCommand() {

			public void execute(Message message) {
				MessageError me = (MessageError) message;
				switch (Constants.ErrorType.valueOf(me.getErrorType())) {
					case AuthFailed:
						switch (context.getGameState()) {
							case titleScreen:
								status = "Connected to server but not authenticated.";
								context.setAuthenticated(AuthStatus.failed);
								context.showAlert("You are not logged in on Towerdefence.net:\n"
										+ "Only single player available.");
						}
						doSendLater();
						break;
					case ReplayLoadFailed:
						if (context.getGameState() == GameState.loadReplay) {
							status = "Failed to load replay!";
							context.setGameState(GameState.titleScreen);
						}
						break;

				}
			}
		});
		messageCommands.put(MessageSettings.key, new MessageCommand() {

			public void execute(Message message) {
				MessageSettings mst = (MessageSettings) message;
				for (Entry<String, String> entry : mst.getSettings().entrySet()) {
					switch (Constants.settingKey.valueOf(entry.getKey())) {
						case ready:
							UserInfo player = usersByUserId.get(mst.getSenderId());
							if (player != null) {
								player.ready = Boolean.parseBoolean(entry.getValue());
							}
							break;
					}
				}
				context.setSettings(mst.getSettings());
			}
		});
		messageCommands.put(MessagePong.key, new MessageCommand() {

			public void execute(Message message) {
			}
		});
		messageCommands.put(MessageReplay.key, new MessageCommand() {

			public void execute(Message message) {
				MessageReplay mrb = (MessageReplay) message;
				if (context.getGameState() == GameState.loadReplay) {
					context.loadReplay(mrb.getReplay());
				}
			}
		});
		messageCommands.put(MessageUnknown.key, new MessageCommand() {

			public void execute(Message message) {
				try {
					System.out.println("PanelNetConnection::textReceived: Unknown message:\n" + message.getMessage());
				} catch (Exception ex) {
					Logger.getLogger(PanelNetConnection.class.getName()).log(Level.SEVERE, null, ex);
				}
			}
		});
	}

	private void textReceived(int connection, String line) {
		if (keepFullLog) {
			fullLog.append(" <-- ");
			fullLog.append(line);
			fullLog.append("\n");
		}

		Message message = messageFactory.parseString(line);
		MessageCommand command = messageCommands.get(message.getKey());
		if (command != null) {
			command.execute(message);
		} else {
			context.messageReceived(message);
		}
	}

	@Override
	public void sendLine(String line) {
		if (client == null && wasConnected) {
			startClient();
		}
		if (client != null) {
			client.sendLine(line);
			if (keepFullLog) {
				fullLog.append(" --> ");
				fullLog.append(line);
				fullLog.append("\n");
			}
		}
	}

	public void transmitSetting(Constants.settingKey key, String value) {
		try {
			Map<String, String> table = new HashMap<String, String>();
			table.put(key.name(), value);
			sendLine(MessageSettings.formatMessage(localUserInfo.userId, table));
		} catch (UnsupportedEncodingException ex) {
			Logger.getLogger(PanelNetConnection.class.getName()).log(Level.SEVERE, null, ex);
		}
	}

	public void transmitSetting(Map<String, String> table) {
		try {
			sendLine(MessageSettings.formatMessage(localUserInfo.userId, table));
		} catch (UnsupportedEncodingException ex) {
			Logger.getLogger(PanelNetConnection.class.getName()).log(Level.SEVERE, null, ex);
		}
	}

	public void sendUpdatedPlayerInfo() {
		sendLine(MessageUserInfo.formatMessage(localUserInfo.userId, localUserInfo));
	}

	private synchronized void addOutputText(String text) {
		htmlContent.insert(0, text);
		jTextPane_output.setText("<html><body>" + htmlContent + "</body></html>");
		jTextPane_output.setCaretPosition(0);
	}

	@Override
	public void connectionEstablished(NetworkConnection connection) {
		status = "Connected to server, authenticating...";
		//addOutputText("<-- " + connection.getId() + " New Connection!");
		final NetworkConnection newConnection = connection;
		connection.addLineListener(new LineListener() {

			@Override
			public void lineReceived(String line) {
				textReceived(newConnection.getId(), line);
			}

			@Override
			public void inputStopped() {
			}
		});
		context.newConnection(connection.getId());
		wasConnected = true;

		if (client != null) {
			sendLine(MessageUserAuth.formatMessage(localUserInfo.token));
		}
	}

	@Override
	public void connectionFailed(NetworkConnection connection, String reason) {
		status = "Connecting to server failed.";
		addOutputText("<div class='playerJoined'>Connection Failed because: " + reason + "</div>");
		context.setConnected(false);
		client.stopClient();
		client = null;
	}

	@Override
	public void connectionLost(NetworkConnection connection) {
		context.setConnected(false);
		context.setAuthenticated(AuthStatus.unknown);
		status = "Not connected to server.";
		client.stopClient();
		client = null;
		addOutputText("<div class='playerJoined'>Lost Connection!</div>");
		switch (context.getGameState()) {
			case mpPlaying:
			case mpWatching:
			case mpCatchup:
			case mpSelecting:
			case mpGameEnd:
				context.setGameState(GameState.titleScreen);
		}
	}

	public synchronized void loadReplay(boolean mp, int replayId) {
		String message;
		if (mp) {
			message = MessageFetch.formatMessage(Constants.FetchType.mp.name(), replayId);
		} else {
			message = MessageFetch.formatMessage(Constants.FetchType.sp.name(), replayId);
		}

		if (context.getAuthenticated() == AuthStatus.unknown) {
			sendLater = message;
		} else {
			sendLine(message);
		}
	}

	private synchronized void doSendLater() {
		if (sendLater != null) {
			sendLine(sendLater);
			sendLater = null;
		}
	}

	private void doSay() {
		String text = jTextField_send.getText();
		try {
			//connector.sendLine("say " + context.getLocalPlayerId() + " " + text);
			sendLine(MessageSay.formatMessage(localUserInfo.userId, text));
		} catch (UnsupportedEncodingException ex) {
			Logger.getLogger(PanelNetConnection.class.getName()).log(Level.SEVERE, null, ex);
		}
		addOutputText("<span class='p" + playerGameId + "'>" + localUserInfo.name + "</span>: " + HTMLEncode.encode(text) + "<br>");
		jTextField_send.setText("");
	}

	public String getToken() {
		return localUserInfo.token;
	}

	public void setToken(String t) {
		localUserInfo.token = t;
	}

	public String getPlayerName() {
		return localUserInfo.name;
	}

	private void kickPlayer() {
		UserInfo selected = (UserInfo) jListPlayerList.getSelectedValue();
		if (selected != null) {
			sendLine(MessageKickUser.formatMessage(selected.userId));
		}
	}

	@Override
	public void setVisible(boolean aFlag) {
		if (aFlag) {
			if (pingTimer == null) {
				pingTimer = new Timer("pingTimer", true);
			}
			if (pingTask == null) {
				pingTask = new TimerTask() {

					@Override
					public void run() {
						sendLine(MessagePing.formatMessage());
					}
				};
				pingTimer.schedule(pingTask, 120000, 120000);
			}
		} else {
			if (pingTask != null) {
				pingTask.cancel();
				pingTask = null;
			}
		}
		super.setVisible(aFlag);
	}

	public void showTextBar() {
		jTextField_send.setVisible(true);
		jTextField_send.requestFocusInWindow();
		jButton_send.setVisible(true);
	}

	public void hideTextBar() {
		if (!keepTextBar) {
			jTextField_send.setVisible(false);
			jButton_send.setVisible(false);
		}
	}

	/** This method is called from within the constructor to
	 * initialize the form.
	 * WARNING: Do NOT modify this code. The content of this method is
	 * always regenerated by the Form Editor.
	 */
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {
        java.awt.GridBagConstraints gridBagConstraints;

        jFrame1 = new javax.swing.JFrame();
        jScrollPane4 = new javax.swing.JScrollPane();
        jTextPaneFullLog = new javax.swing.JTextPane();
        jButton1 = new javax.swing.JButton();
        jButtonKick = new javax.swing.JButton();
        jPanelChat = new javax.swing.JPanel();
        jScrollPane1 = new javax.swing.JScrollPane();
        jTextPane_output = new javax.swing.JTextPane();
        jTextField_send = new javax.swing.JTextField();
        jButton_send = new javax.swing.JButton();
        jScrollPane2 = new javax.swing.JScrollPane();
        jListPlayerList = new javax.swing.JList();
        jPanelChannels = new javax.swing.JPanel();
        jPanelInLobby = new javax.swing.JPanel();
        jScrollPane3 = new javax.swing.JScrollPane();
        jListChannelList = new javax.swing.JList();
        jButtonJoin = new javax.swing.JButton();
        jButtonCreate = new javax.swing.JButton();
        jButtonExit = new javax.swing.JButton();

        jScrollPane4.setViewportView(jTextPaneFullLog);

        jFrame1.getContentPane().add(jScrollPane4, java.awt.BorderLayout.CENTER);

        jButton1.setText("refresh");
        jButton1.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButton1ActionPerformed(evt);
            }
        });
        jFrame1.getContentPane().add(jButton1, java.awt.BorderLayout.SOUTH);

        setLayout(new java.awt.GridBagLayout());

        jButtonKick.setBackground(Constants.colorBackGround);
        jButtonKick.setForeground(Constants.colorForeGround);
        jButtonKick.setText("Kick");
        jButtonKick.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButtonKickActionPerformed(evt);
            }
        });
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 4;
        gridBagConstraints.gridy = 1;
        add(jButtonKick, gridBagConstraints);

        jPanelChat.setBackground(Constants.colorBackGround);
        jPanelChat.setBorder(javax.swing.BorderFactory.createTitledBorder(null, "Chat", javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, javax.swing.border.TitledBorder.DEFAULT_POSITION, new java.awt.Font("Bitstream Vera Sans"Constants.colorForeGround)); // NOI18N
        jPanelChat.setForeground(Constants.colorForeGround);
        jPanelChat.setMaximumSize(new java.awt.Dimension(2147483647, 175));
        jPanelChat.setPreferredSize(new java.awt.Dimension(134, 175));
        jPanelChat.setLayout(new java.awt.GridBagLayout());

        jScrollPane1.setBackground(Constants.colorBackGround);
        jScrollPane1.setForeground(Constants.colorForeGround);
        jScrollPane1.setVerticalScrollBarPolicy(javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);

        jTextPane_output.setBackground(Constants.colorBackGround);
        jTextPane_output.setEditable(false);
        jTextPane_output.setForeground(Constants.colorForeGround);
        jScrollPane1.setViewportView(jTextPane_output);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 1;
        gridBagConstraints.gridwidth = 4;
        gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
        gridBagConstraints.weightx = 0.01;
        gridBagConstraints.weighty = 1.0E-4;
        jPanelChat.add(jScrollPane1, gridBagConstraints);

        jTextField_send.setBackground(Constants.colorBackGround);
        jTextField_send.setForeground(Constants.colorForeGround);
        jTextField_send.setText("Some text");
        jTextField_send.setCaretColor(context.colors.getColorForeGroundPale());
        jTextField_send.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jTextField_sendActionPerformed(evt);
            }
        });
        jTextField_send.addFocusListener(new java.awt.event.FocusAdapter() {
            public void focusLost(java.awt.event.FocusEvent evt) {
                jTextField_sendFocusLost(evt);
            }
        });
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 0;
        gridBagConstraints.gridwidth = 3;
        gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
        gridBagConstraints.weightx = 0.01;
        jPanelChat.add(jTextField_send, gridBagConstraints);

        jButton_send.setBackground(Constants.colorBackGround);
        jButton_send.setForeground(Constants.colorForeGround);
        jButton_send.setText("Send");
        jButton_send.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButton_sendActionPerformed(evt);
            }
        });
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 3;
        gridBagConstraints.gridy = 0;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHEAST;
        jPanelChat.add(jButton_send, gridBagConstraints);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 3;
        gridBagConstraints.gridy = 0;
        gridBagConstraints.gridheight = 2;
        gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTH;
        gridBagConstraints.weightx = 0.1;
        add(jPanelChat, gridBagConstraints);

        jScrollPane2.setBackground(Constants.colorBackGround);
        jScrollPane2.setBorder(javax.swing.BorderFactory.createTitledBorder(null, "Players", javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, javax.swing.border.TitledBorder.DEFAULT_POSITION, new java.awt.Font("Bitstream Vera Sans"Constants.colorForeGround)); // NOI18N
        jScrollPane2.setForeground(Constants.colorForeGround);
        jScrollPane2.setMaximumSize(new java.awt.Dimension(150, 32767));
        jScrollPane2.setMinimumSize(new java.awt.Dimension(0, 0));
        jScrollPane2.setPreferredSize(new java.awt.Dimension(150, 128));

        jListPlayerList.setBackground(Constants.colorBackGround);
        jListPlayerList.setForeground(Constants.colorForeGround);
        jListPlayerList.setModel(new DefaultListModel());
        jListPlayerList.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
        jScrollPane2.setViewportView(jListPlayerList);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 4;
        gridBagConstraints.gridy = 0;
        gridBagConstraints.fill = java.awt.GridBagConstraints.VERTICAL;
        gridBagConstraints.weighty = 1.0E-7;
        add(jScrollPane2, gridBagConstraints);

        jPanelChannels.setBackground(Constants.colorBackGround);
        jPanelChannels.setForeground(Constants.colorForeGround);
        jPanelChannels.setLayout(new java.awt.GridBagLayout());

        jPanelInLobby.setBackground(Constants.colorBackGround);
        jPanelInLobby.setBorder(javax.swing.BorderFactory.createTitledBorder(null, "Games", javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, javax.swing.border.TitledBorder.DEFAULT_POSITION, new java.awt.Font("Bitstream Vera Sans"Constants.colorForeGround)); // NOI18N
        jPanelInLobby.setForeground(Constants.colorForeGround);
        jPanelInLobby.setLayout(new java.awt.GridBagLayout());

        jScrollPane3.setBackground(Constants.colorBackGround);
        jScrollPane3.setForeground(Constants.colorForeGround);
        jScrollPane3.setMaximumSize(new java.awt.Dimension(100, 32767));
        jScrollPane3.setMinimumSize(new java.awt.Dimension(0, 0));
        jScrollPane3.setPreferredSize(new java.awt.Dimension(100, 157));

        jListChannelList.setBackground(Constants.colorBackGround);
        jListChannelList.setForeground(Constants.colorForeGround);
        jListChannelList.setModel(new DefaultListModel());
        jListChannelList.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
        jListChannelList.setCellRenderer(new ChannelListRenderer(context.colors));
        jListChannelList.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                jListChannelListMouseClicked(evt);
            }
        });
        jScrollPane3.setViewportView(jListChannelList);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 0;
        gridBagConstraints.gridwidth = 2;
        gridBagConstraints.gridheight = 3;
        gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
        gridBagConstraints.weightx = 0.01;
        gridBagConstraints.weighty = 0.01;
        jPanelInLobby.add(jScrollPane3, gridBagConstraints);

        jButtonJoin.setBackground(Constants.colorBackGround);
        jButtonJoin.setForeground(Constants.colorForeGround);
        jButtonJoin.setText("Join");
        jButtonJoin.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButtonJoinActionPerformed(evt);
            }
        });
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 3;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.SOUTHWEST;
        gridBagConstraints.insets = new java.awt.Insets(2, 0, 0, 2);
        jPanelInLobby.add(jButtonJoin, gridBagConstraints);

        jButtonCreate.setBackground(Constants.colorBackGround);
        jButtonCreate.setForeground(Constants.colorForeGround);
        jButtonCreate.setText("New");
        jButtonCreate.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButtonCreateActionPerformed(evt);
            }
        });
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 1;
        gridBagConstraints.gridy = 3;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.SOUTHEAST;
        gridBagConstraints.insets = new java.awt.Insets(2, 2, 0, 0);
        jPanelInLobby.add(jButtonCreate, gridBagConstraints);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 2;
        gridBagConstraints.gridy = 0;
        gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTH;
        gridBagConstraints.weightx = 0.01;
        gridBagConstraints.weighty = 0.01;
        jPanelChannels.add(jPanelInLobby, gridBagConstraints);

        jButtonExit.setBackground(Constants.colorBackGround);
        jButtonExit.setForeground(Constants.colorForeGround);
        jButtonExit.setText("Exit Game");
        jButtonExit.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButtonExitActionPerformed(evt);
            }
        });
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 1;
        gridBagConstraints.gridy = 0;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.SOUTH;
        gridBagConstraints.weighty = 0.01;
        jPanelChannels.add(jButtonExit, gridBagConstraints);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridheight = 2;
        gridBagConstraints.fill = java.awt.GridBagConstraints.VERTICAL;
        add(jPanelChannels, gridBagConstraints);
    }// </editor-fold>//GEN-END:initComponents

	private void jButton_sendActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton_sendActionPerformed
		doSay();
		if (keepFullLog) {
			jTextPaneFullLog.setText(fullLog.toString());
			jFrame1.setVisible(true);
			jFrame1.pack();
		}
	}//GEN-LAST:event_jButton_sendActionPerformed

	private void jTextField_sendActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jTextField_sendActionPerformed
		doSay();
	}//GEN-LAST:event_jTextField_sendActionPerformed

	private void jButtonJoinActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButtonJoinActionPerformed
		doJoin();
	}//GEN-LAST:event_jButtonJoinActionPerformed

	private void jButtonCreateActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButtonCreateActionPerformed
		String s = JOptionPane.showInputDialog(
				"Please enter a name for your game",
				localUserInfo.name + "'s Game");
		if ((s != null) && (s.length() > 0)) {
			try {
				sendLine(MessageGameCreate.formatMessage(s));
			} catch (UnsupportedEncodingException ex) {
				Logger.getLogger(PanelNetConnection.class.getName()).log(Level.SEVERE, null, ex);
			}
		}
	}//GEN-LAST:event_jButtonCreateActionPerformed

	private void jButtonExitActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButtonExitActionPerformed
		exitGame();
	}//GEN-LAST:event_jButtonExitActionPerformed

	private void jListChannelListMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_jListChannelListMouseClicked
		if (evt.getClickCount() > 1) {
			doJoin();
		}
	}//GEN-LAST:event_jListChannelListMouseClicked

	private void jButtonKickActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButtonKickActionPerformed
		kickPlayer();
	}//GEN-LAST:event_jButtonKickActionPerformed

	private void jTextField_sendFocusLost(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_jTextField_sendFocusLost
		hideTextBar();
	}//GEN-LAST:event_jTextField_sendFocusLost

	private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton1ActionPerformed
		jTextPaneFullLog.setText(fullLog.toString());
	}//GEN-LAST:event_jButton1ActionPerformed
    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JButton jButton1;
    private javax.swing.JButton jButtonCreate;
    private javax.swing.JButton jButtonExit;
    private javax.swing.JButton jButtonJoin;
    private javax.swing.JButton jButtonKick;
    private javax.swing.JButton jButton_send;
    private javax.swing.JFrame jFrame1;
    private javax.swing.JList jListChannelList;
    private javax.swing.JList jListPlayerList;
    private javax.swing.JPanel jPanelChannels;
    private javax.swing.JPanel jPanelChat;
    private javax.swing.JPanel jPanelInLobby;
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JScrollPane jScrollPane2;
    private javax.swing.JScrollPane jScrollPane3;
    private javax.swing.JScrollPane jScrollPane4;
    private javax.swing.JTextField jTextField_send;
    private javax.swing.JTextPane jTextPaneFullLog;
    private javax.swing.JTextPane jTextPane_output;
    // End of variables declaration//GEN-END:variables

	/**
	 * @return the status
	 */
	public String getStatus() {
		return status;
	}

	/**
	 * @return the keepTextBar
	 */
	public boolean isKeepTextBar() {
		return keepTextBar;
	}

	/**
	 * @param keepTextBar the keepTextBar to set
	 */
	public void setKeepTextBar(boolean ktb) {
		if (keepTextBar != ktb) {
			keepTextBar = ktb;
			jTextField_send.setVisible(ktb);
			jButton_send.setVisible(ktb);
		}
	}

	/**
	 * @return the localPlayerInfo
	 * After making changes to the local player info,
	 * @see sendUpdatedPlayerInfo() must be called to send the changes to the
	 * server.
	 */
	public UserInfo getLocalPlayerInfo() {
		return localUserInfo;
	}
}
